package heiing.collection.map;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.IntFunction;
import java.util.function.Supplier;
import static heiing.collection.util.CollectionUtils.subtract;
import java.util.function.Function;

/**
 *
 * @author heiing
 */
public class MapUtils {

    private MapUtils() {}

    /**
     * 将集合转换成字典
     * @param <K> 字典Key的类型
     * @param <V> 集合元素与字典Value的类型
     * @param items 待转换的集合
     * @param keyMapper 把集合元素映射到 Key 的方法
     * @return 返回转换后的 HashMap。如果集合为空，则返回空字典
     */
    public static <K, V> Map<K, V> toMap(Collection<V> items, Function<V, K> keyMapper) {
        return toMap(items, keyMapper, HashMap::new);
    }

    /**
     * 将集合转换成字典
     * @param <K> 字典Key的类型
     * @param <V> 集合元素与字典Value的类型
     * @param items 待转换的集合
     * @param keyMapper 把集合元素映射到 Key 的方法
     * @param supplier 创建结果字典的方法，例如 HashMap::new 则生成 new HashMap<>(items.size())
     * @return 返回转换后的根据 supplier 生成的字典。如果集合为空，则返回空字典
     */
    public static <K, V> Map<K, V> toMap(Collection<V> items, Function<V, K> keyMapper, IntFunction<Map<K, V>> supplier) {
        if (null == items || items.isEmpty()) {
            return Collections.emptyMap();
        }
        Map<K, V> ret = supply(items.size(), supplier);
        items.stream().filter(Objects::nonNull).forEach(v -> ret.put(keyMapper.apply(v), v));
        return ret;
    }

    /**
     * 将列表组合成 HashMap
     * @param <K> 键类型
     * @param <V> 值类型
     * @param keys 键列表
     * @param values 值列表
     * @return 返回以 keys 为键， values 为值的 HashMap
     */
    public static <K, V> Map<K, V> combine(Collection<K> keys, Collection<V> values) {
        return combine(keys, values, HashMap::new);
    }

    /**
     * 将列表组合成 Map，由 supplier 决定 Map 的实现类型
     * @param <K> 键类型
     * @param <V> 值类型
     * @param keys 键列表
     * @param values 值列表
     * @param supplier 创建Map的方法，例如 HashMap::new
     * @return
     */
    public static <K, V> Map<K, V> combine(Collection<K> keys, Collection<V> values, IntFunction<Map<K, V>> supplier) {
        Objects.requireNonNull(keys, "keys 不能为 null");
        Objects.requireNonNull(values, "values 不能为 null");
        int size = keys.size();
        if (size != values.size()) {
            throw new IllegalArgumentException("keys 的个数必须要与 values 的个数相同");
        }
        Map<K, V> map = supply(size, supplier);
        Iterator<K> k = keys.iterator();
        Iterator<V> v = values.iterator();
        while (k.hasNext()) {
            map.put(k.next(), v.next());
        }
        return map;
    }

    /**
     * 根据 Key 计算 a 与 b 的差集，即 a - b。
     * @param <K> Key的类型
     * @param <V> Value的类型
     * @param a 字典a
     * @param b 字典b
     * @param supplier 结果列表的类型与集合容器生成方法，例如 HashMap::new
     * @return 返回  a - b 的结果，如果 b 为空，则返回 a，如果 a 为空，则返回空字典
     */
    public static <K, V> Map<K, V> subtractByKey(final Map<K, V> a, final Map<K, V> b, Supplier<Map<K, V>> supplier) {
        if (null == a || null == b || b.isEmpty()) {
            return null == a ? Collections.emptyMap() : a;
        }
        Set<K> diffs = subtract(new LinkedHashSet<>(a.keySet()), new LinkedHashSet<>(b.keySet()), LinkedHashSet::new);
        return diffs.stream().collect(supplier, (m, k) -> m.put(k, a.get(k)), Map::putAll);
    }

    private static <K, V> Map<K, V> supply(int size, IntFunction<Map<K, V>> supplier) {
        return null == supplier ? new HashMap<>(size) : supplier.apply(size);
    }

}
